home *** CD-ROM | disk | FTP | other *** search
- /*
- * CBLibrary - RoundRobin
- * Copyright (C) 2003 Chris Bazley
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
- /* Round-robin system for multiple threads that require null polls */
-
- /* ANSI library files */
- #include <stdlib.h>
- #include <string.h>
- #include <stdbool.h>
- #ifndef NDEBUG
- #include <stdio.h>
- #endif
- #include <assert.h>
-
- /* RISC OS library files */
- #include "kernel.h"
- #include "wimp.h"
- #include "event.h"
- #include "toolbox.h"
-
- /* My library files */
- #include "Macros.h"
- #include "err.h"
- #include "NullPoll.h"
- #include "timer.h"
- #include "RoundRobin.h"
-
- typedef struct _RoundRobin_record {
- RoundRobinHandler *handler; /* == NULL marks free blocks */
- void *handle;
- } RoundRobin_record;
-
- extern _kernel_oserror shared_err_block;
-
- static int suspended;
- static RoundRobin_record *threads_array;
- static unsigned int threads_array_len, thread_to_call, num_threads, maxtime;
- static volatile bool time_up = true;
-
- /* ----------------------------------------------------------------------- */
- /* Function prototypes */
-
- static WimpEventHandler _RoundRobin_handler;
- static void ensure_timer_removed(void);
-
- /* ----------------------------------------------------------------------- */
- /* Public functions */
-
- _kernel_oserror *RoundRobin_initialise(unsigned int time)
- {
- THROW(event_register_wimp_handler(-1, Wimp_ENull, _RoundRobin_handler, NULL))
- thread_to_call = 0;
- threads_array_len = 0;
- threads_array = NULL;
- suspended = 0;
- maxtime = time;
- atexit(ensure_timer_removed);
- return NULL;
- }
-
- /* ----------------------------------------------------------------------- */
-
- #ifdef INCLUDE_FINALISATION_CODE
- _kernel_oserror *RoundRobin_finalise(void)
- {
- THROW(event_deregister_wimp_handler(-1, Wimp_ENull, _RoundRobin_handler, NULL))
- free(threads_array);
- threads_array_len = 0;
- if(num_threads > 0 && suspended != 0)
- nullpoll_deregister();
-
- return NULL;
- }
- #endif
-
- /* ----------------------------------------------------------------------- */
-
- _kernel_oserror *RoundRobin_register(RoundRobinHandler *handler, void *handle)
- {
- /* Add record to threads data */
- RoundRobin_record *write_data = NULL;
-
- if(threads_array != NULL) {
- /* Search for a free RoundRobin_record block in array */
- unsigned int i;
- for(i = 0; i < threads_array_len && write_data == NULL; i++) {
- if(threads_array[i].handler == NULL)
- write_data = &(threads_array[i]); /* found one */
- }
- }
-
- if(write_data == NULL) {
- /* Create/Extend array of RoundRobin_record blocks */
- RoundRobin_record *new_data = realloc(threads_array, sizeof(RoundRobin_record)*(threads_array_len+1));
- if(new_data == NULL) {
- WRITE_GERR(shared_err_block, "NoMem");
- return &shared_err_block;
- }
- threads_array = new_data;
- write_data = &(threads_array[threads_array_len]);
- threads_array_len++;
- write_data->handler = NULL; /* mark new (end) block as free */
- }
-
- if(num_threads == 0 && suspended == 0)
- nullpoll_register(); /* We have first client */
- num_threads++;
-
- /* Fill out fields of RoundRobin_record block */
- write_data->handler = handler;
- write_data->handle = handle;
-
- return NULL; /* success */
- }
-
- /* ----------------------------------------------------------------------- */
-
- void RoundRobin_deregister(RoundRobinHandler *handler, void *handle)
- {
- RoundRobin_record *write_data = NULL;
-
- /* Search for the specified thread data */
- {
- unsigned int i;
- for(i = 0; i < threads_array_len && write_data == NULL; i++) {
- if(threads_array[i].handler == handler
- && threads_array[i].handle == handle)
- write_data = &(threads_array[i]); /* found it */
- }
- }
- assert(write_data != NULL);
- if(write_data == NULL)
- return; /* bad deregistration */
-
- if(num_threads == 1 && suspended == 0)
- nullpoll_deregister(); /* We have run out of clients */
- num_threads--;
-
- write_data->handler = NULL; /* mark slot as free */
- }
-
- /* ----------------------------------------------------------------------- */
-
- void RoundRobin_suspend(void)
- {
- if(suspended == 0 && num_threads > 0)
- nullpoll_deregister();
- suspended++;
- }
-
- /* ----------------------------------------------------------------------- */
-
- void RoundRobin_resume(void)
- {
- /* Can't resume if not suspended! */
- assert(suspended >= 1);
- if(suspended < 1)
- return; /* not suspended */
-
- if(suspended == 1 && num_threads > 0)
- nullpoll_register();
-
- suspended--;
- }
-
-
- /* ----------------------------------------------------------------------- */
- /* Private functions */
-
- static int _RoundRobin_handler(int event_code, WimpPollBlock *event, IdBlock *id_block, void *handle)
- {
- NOT_USED(event_code)
- NOT_USED(event)
- NOT_USED(id_block)
- NOT_USED(handle)
- #ifndef NDEBUG
- int num_this_poll = 0;
- #endif
-
- if(threads_array == NULL || suspended != 0)
- return 0; /* nothing to do - pass event on */
-
- {
- _kernel_oserror *err;
- time_up = false;
- err = timer_register(&time_up, maxtime);
- if(err != NULL) {
- time_up = true; /* could not set up timer event */
- err_check_rep(err);
- }
- }
-
- #ifndef NDEBUG
- _kernel_oscli("report Entering RoundRobin null event dispatcher");
- #endif
-
- do {
- RoundRobin_record *block;
-
- if(thread_to_call >= threads_array_len)
- thread_to_call = 0;
-
- block = &threads_array[thread_to_call];
- #ifndef NDEBUG
- {
- char string[255];
- sprintf(string, "report Thread record %d at %p", thread_to_call, block);
- _kernel_oscli(string);
- }
- #endif
-
- if(block->handler != NULL) {
- #ifndef NDEBUG
- char string[255];
- sprintf(string, "report Calling handler %p with handle %p", block->handler, block->handle);
- _kernel_oscli(string);
- num_this_poll++;
- #endif
- block->handler(block->handle, &time_up);
- }
-
- thread_to_call++;
- } while (!time_up);
- ensure_timer_removed(); /* in case OS_CallAfter event still pending */
-
- #ifndef NDEBUG
- {
- char string[255];
- sprintf(string, "report Handlers called this poll: %d", num_this_poll);
- _kernel_oscli(string);
- }
- #endif
- return 0; /* pass event on */
- }
-
- /* ----------------------------------------------------------------------- */
-
- static void ensure_timer_removed(void)
- {
- /* Last-ditch effort to remove OS_CallAfter routine, if still pending */
- if(!time_up) {
- timer_deregister(&time_up);
- time_up = true;
- }
- /* (suppress errors, as event may occur between check and removal) */
- }
-